/*
 * Copyright 2010 Alessandro Bellucci
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.soupframework.gwt.designerpanel.client.designer;

import java.util.List;

import org.soupframework.gwt.designerpanel.client.DesignerPanel;
import org.soupframework.gwt.designerpanel.client.event.HasChildDesignerAddedHandler;
import org.soupframework.gwt.designerpanel.client.event.HasObjectAddedHandler;
import org.soupframework.gwt.designerpanel.client.event.HasObjectRemovedHandler;
import org.soupframework.gwt.designerpanel.client.event.HasObjectSelectionChangedHandler;
import org.soupframework.gwt.designerpanel.client.event.ObjectSelectionChangedEvent;

import com.google.gwt.user.client.ui.Widget;

public interface ContainerDesigner<T> extends Designer<T>, HasObjectSelectionChangedHandler<T>, HasObjectAddedHandler<T>, HasObjectRemovedHandler<T>, HasChildDesignerAddedHandler<T> {

	public interface Operations<T> extends Designer.Operations<T> {
		/**
		 * Adds the object to the {@link ContainerDesigner}
		 *
		 * @param object the object to add
		 * @param left the left coordinate of the upper left corner
		 * @param top the top coordinate of the upper left corner
		 */
		public void addObject(T object, int left, int top);

		/**
		 * Adds the object to the {@link ContainerDesigner} at the specified position index
		 *
		 * @param object the object to add
		 * @param left the left coordinate of the upper left corner
		 * @param top the top coordinate of the upper left corner
		 * @param index the position index
		 */
		public void addObject(T object, int left, int top, int index);

		/**
		 * Resize the graphical representation of the given object to the given size
		 *
		 * @param object the object to resize
		 * @param width the width of the graphical representation
		 * @param height the height of the graphical representation
		 */
		public void resizeObjectView(T  object, int width, int height);

		/**
		 * Removes the object from the {@link ContainerDesigner}
		 *
		 * @param object the object to remove
		 */
		public void removeObject(T object);

		/**
		 * Exchange the given objects in the list of children
		 *
		 * @param object1 The first object.
		 * @param object2 The second object.
		 */
		public void exchangeObjects(T object1, T object2);
	}

	@Override
	public Operations<T> getOperations();

	/**
	 * Adds a child {@link Designer} to this {@link Designer}
	 *
	 * @param childDesigner The child {@link Designer}
	 */
	public void addChildDesigner(Designer<T> childDesigner);

	/**
	 * Gets the children designers for this designer
	 *
	 * @return the list of children designers
	 */
	public List<Designer<T>> getChildrenDesigners();

	/**
	 * Gets the child designer for a given child object
	 *
	 * @param object The child object
	 * @return the child {@link Designer} for the given child {@link Widget} or null if w is not a child object or there is no {@link Designer} associated with w object
	 */
	public Designer<T> getChildDesigner(T object);

	/**
	 * Removes the child designer
	 *
	 * @param d The child {@link Designer}
	 */
	public void removeChildDesigner(Designer<T> childDesigner);

	/**
	 * Filter children designers that are instances of {@link ContainerDesigner}
	 *
	 * @return the list of children container designers
	 */
	public List<ContainerDesigner<T>> getChildrenContainerDesigners();

	/**
	 * Gets the child objects of the container
	 *
	 * @return The list of child objects
	 */
	public List<T> getChildrenObjects();

	/**
	 * Checks whether the given object is a child object of this container
	 *
	 * @param object the object
	 * @return true if the given object is a child of this container, false otherwise
	 */
	public boolean hasChildObject(T object);

	/**
	 * Gets the {@link Widget} that graphically represents the given child object (the view)
	 *
	 * @param object the child object
	 * @return the {@link Widget} representing the child object
	 */
	public Widget getChildObjectView(T object);

	/**
	 * Checks whether the given {@link Designer} is a descendant of this {@link Designer}
	 *
	 * @return true if passed {@link Designer} is a descendant of this
	 */
	public boolean isDescendantDesigner(Designer<T> designer);

	public void setWidth(String width);

	public void setHeight(String height);

	/**
	 * Adds the given object to the container
	 *
	 * @param object the object to add
	 * @param left the left coordinate of the upper left corner of the object representation
	 * @param top the top coordinate of the upper left corner of the object representation
	 */
	public void addObject(T object, int left, int top);

	/**
	 * Resize the graphical representation of the given object
	 *
	 * @param object the object to resize
	 * @param width the width of the widget
	 * @param height the height of the widget
	 */
	public void resizeObject(T object, int width, int height);

	/**
	 * Resize the graphical representation of the give object. It differs from the resizeObject method in that subsequent calls to resizeObjectPartial
	 * cumulate to the same command group in history
	 *
	 * @param object the object to resize
	 * @param width the width of the object
	 * @param height the height of the object
	 */
	public void resizeObjectPartial(String commandGroupTypePrefix, T object, int width, int height);

	/**
	 * Register the object as a child object
	 *
	 * @param object the child object to register
	 */
	public void registerChildObject(T object);

	/**
	 * Removes the given object from the container
	 *
	 * @param object the object to remove
	 */
	public void removeObject(T object);

	/**
	 * Removes the given object from the source container and adds it to this container
	 *
	 * @param sourceContainer the source con
	 * @param object the object to move
	 * @param left the left coordinate of the upper left corner
	 * @param top the top coordinate of the upper left corner
	 */
	public void moveFromContainer(ContainerDesigner<T> sourceDesigner, T object, int left, int top);

	/**
	 * Removes all objects from the container
	 */
	public void clear();

	/**
	 * Return the objects that contains the specified coords
	 *
	 * @param x Coordinate x
	 * @param y Coordinate y
	 * @return the objects containing the coords
	 */
	public Iterable<T> getObjectsAtCoords(int x, int y);

	/**
	 * Return the foremost object that contains the specified coords
	 *
	 * @param x Coordinate x
	 * @param y Coordinate y
	 * @return the foremost object containing the coords
	 */
	public T getForemostObjectAtCoords(int x, int y);

	/**
	 * Return the objects that intersects the given area
	 *
	 * @param startX Start coordinate x
	 * @param startY Start coordinate y
	 * @param endX End coordinate x
	 * @param endY End coordinate y
	 * @return the objects intersecting the area
	 */
	public Iterable<T> getObjectsInArea(int startX, int startY, int endX, int endY);

	/**
	 * Clears the selection (going deep to descendant designers) and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 */
	public void clearSelection();

	/**
	 * Clears the selection (going deep to descendant designers) and possibly fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param fireEvent true if a {@link ObjectSelectionChangedEvent} should be fired
	 */
	public void clearSelection(boolean fireEvent);

	/**
	 * Clears the selection and possibly fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param fireEvent true if a {@link ObjectSelectionChangedEvent} should be fired
	 * @param deep if true the selection is cleared going deep to descendant designers
	 */
	public void clearSelection(boolean fireEvent, boolean deep);

	/**
	 * Set selected objects for {@link DesignerPanel} and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to select
	 */
	public void setSelection(Iterable<T> os);

	/**
	 * Set selected objects for {@link DesignerPanel}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to select
	 * @param fireEvent true if a WidgetSelectionChangedEvent should be fired after selection
	 */
	public void setSelection(Iterable<T> os, boolean fireEvent);

	/**
	 * Checks whether the specified object is selected or not
	 *
	 * @param object The object to check for selection
	 * @return true if the specified object is part of the selection, false otherwise
	 */
	public boolean isObjectSelected(T object);

	/**
	 * Checks whether the specified objects are selected or not
	 *
	 * @param os The objects to check for selection
	 * @return true if all specified widgets are part of the selection, false otherwise
	 */
	public boolean areObjectsSelected(Iterable<T> os);

	/**
	 * Removes specified object from selection and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param object The object to remove from selection
	 */
	public void removeObjectFromSelection(T object);

	/**
	 * Removes specified object from selection
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param object The object to remove from selection
	 * @param fireEvent true if a {@link ObjectSelectionChangedEvent} should be fired after selection
	 */
	public void removeObjectFromSelection(T object, boolean fireEvent);

	/**
	 * Removes specified objects from selection and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to remove from selection
	 */
	public void removeObjectsFromSelection(Iterable<T> ws);

	/**
	 * Removes specified objects from selection
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to remove from selection
	 * @param fireEvent true if a WidgetSelectionChangedEvent should be fired after selection
	 */
	public void removeObjectsFromSelection(Iterable<T> os, boolean fireEvent);

	/**
	 * Adds specified objects to selection and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param object The object to add to selection
	 */
	public void addObjectToSelection(T object);

	/**
	 * Adds specified object to selection
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param object The object to add to selection
	 * @param fireEvent true if a WidgetSelectionChangedEvent should be fired after selection
	 */
	public void addObjectToSelection(T object, boolean fireEvent);

	/**
	 * Adds specified objects to selection and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to add to selection
	 */
	public void addObjectsToSelection(Iterable<T> ws);

	/**
	 * Adds specified objects to selection
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects to add to selection
	 * @param fireEvent true if a {@link ObjectSelectionChangedEvent} should be fired after selection
	 */
	public void addObjectsToSelection(Iterable<T> os, boolean fireEvent);

	/**
	 * Toggles selection state for specified objects and fires a {@link ObjectSelectionChangedEvent}
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects whose selection state should be toggled
	 */
	public void toggleObjectsSelection(Iterable<T> os);

	/**
	 * Toggles selection state for specified objects
	 * This operation is a no-op if operating mode is LIVE
	 *
	 * @param os The objects whose selection state should be toggled
	 * @param fireEvent true if a {@link ObjectSelectionChangedEvent} should be fired after selection
	 */
	public void toggleObjectsSelection(Iterable<T> os, boolean fireEvent);

	/**
	 * Returns the number of selected objects
	 *
	 * @param deep if true selected objects in descendant container designers are counted
	 * @return The number of selected objects
	 */
	public int getSelectedObjectsCount(boolean deep);

	/**
	 * Returns selected objects
	 *
	 * @param deep if true descendant container designers are scanned for selection
	 * @return selected objects
	 */
	public List<T> getSelectedObjects(boolean deep);

	/**
	 * Shows a drag proxy for the given object at the given position.
	 *
	 * @param object The object being dragged
	 * @param objectView The view of the object being dragged
	 * @param left The left drag coord
	 * @param top The top drag coord
	 */
	public void showDragProxy(T object, Widget objectView, int left, int top);

	/**
	 * Hides the drag proxy
	 */
	public void hideDragProxy();

	/**
	 * Checks whether dropping the given object on the container at the specified coords is allowed.
	 *
	 * @param object The object to drop
	 * @param left The left coordinate
	 * @param top The top coordinate
	 * @return true if drop is allowed at the given coords, false otherwise
	 */
	public boolean isDropAllowed(T object, int left, int top);

	/**
	 * Returns true if the given object can be dropped on the container at the specified coords.
	 * Can include user confirmation.
	 *
	 * @param object The object to drop
	 * @param left The left coordinate
	 * @param top The top coordinate
	 * @return true if drop is accepted at the given coords, false otherwise
	 */
	public boolean acceptDrop(T object, int left, int top);

	/**
	 * Open context menu for selected {@link Widget}s
	 * 
	 * @param preferredLeft the preferred left coordinate for the context menu
	 * @param preferredTop the preferred top coordinate for the context menu
	 */
	public void openSelectionContextMenu(int preferredLeft, int preferredTop);
}
